package perform;

import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import perform.connection.ConnectionManager;
import perform.database.DatabaseProcessor;
import perform.event.PerformEventHandler;
import perform.exception.ConfigurationException;
import perform.exception.ConnectionException;
import perform.mappings.ClassMappings;
import perform.query.builder.QueryBuilder;
import perform.startup.ExtendedStartupBroker;
import perform.startup.SimpleStartupBroker;
import perform.startup.StartupBroker;
import perform.type.Type;
import perform.utilities.Configuration;

public class Caretaker {
	
	private static volatile Caretaker instance;
	
	private static volatile Configuration configuration;
	private static volatile ConnectionManager pool;
	private static volatile StartupBroker startupBroker;
	
	private Caretaker() throws ConfigurationException, ConnectionException, SQLException{
		configuration = new Configuration();
		prepare();
	}
	private Caretaker(Properties properties) throws ConfigurationException, ConnectionException, SQLException{
		configuration = new Configuration();
		configuration.setProperties(properties);
		prepare();
	}
	public static Caretaker initialize(){
		if(instance == null){
			System.out.println("[Perform] Perform engine started at: " + new Date(System.currentTimeMillis()).toString());
			try {
				instance = new Caretaker();
			} catch (ConfigurationException e) {
				e.printStackTrace();
				return null;
			} catch (ConnectionException e) {
				e.printStackTrace();
				return null;
			} catch (SQLException e) {
				e.printStackTrace();
				return null;
			}
		}
		return instance;
	}
	
	public static Caretaker initialize(Properties properties) {
		if(instance == null){
			System.out.println("[Perform] Perform engine started at: " + new Date(System.currentTimeMillis()).toString());
			try {
				instance = new Caretaker(properties);
				System.out.println("[Perform] Perform engine initialized at: " + new Date(System.currentTimeMillis()).toString());
			} catch (ConfigurationException e) {
				e.printStackTrace();
				return null;
			} catch (ConnectionException e) {
				e.printStackTrace();
				return null;
			} catch (SQLException e) {
				e.printStackTrace();
				return null;
			}
		}
		return instance;
	}
	public static synchronized void cleanUp(){
		try {
			if(getProperty("drop").equals("true")){
				startupBroker.drop();
			}
			pool.close();
			System.out.println("[Perform] Perform engine stopped at: " + new Date(System.currentTimeMillis()).toString());
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	private void prepare() throws ConnectionException, SQLException{
		pool = new ConnectionManager(configuration.getProperty("host"), configuration.getProperty("port"), 
				configuration.getProperty("name"), configuration.getProperty("username"), 
				configuration.getProperty("password"), configuration.getProperty("min-pool-size"), 
				configuration.getProperty("acquire-increment"), configuration.getProperty("max-pool-size"));
		PerformEventHandler.setPool(pool);
		buildMappings();
		prepareDatabase();
	}
	private void buildMappings(){
		List<Class<? extends Type>> classes = configuration.getClasses();
		for(int i = 0; i < classes.size(); i++){
			ClassMappings.addClass(classes.get(i));
		}
		ClassMappings.finalizeMappings();
		for(int i = 0; i < classes.size(); i++){
			QueryBuilder.buildQueries(classes.get(i));
		}
	}
	private void prepareDatabase() throws SQLException{
		DatabaseProcessor databaseProcessor = new DatabaseProcessor(getProperties());
		databaseProcessor.generateQueries(getClasses());
		if(getProperty("writeSQL").equals("true")){
			startupBroker = new ExtendedStartupBroker(pool.getStartupConnection(), databaseProcessor);
		} else {
			startupBroker = new SimpleStartupBroker(pool.getStartupConnection(), databaseProcessor);
		}
		startupBroker.prepareDatabase();
	}
	
	static ConnectionManager getPool() {
		return pool;
	}
	public static Properties getProperties(){
		return configuration.getProperties();
	}
	public static String getProperty(String key){
		return configuration.getProperty(key);
	}
	public static List<Class<? extends Type>> getClasses(){
		return configuration.getClasses();
	}

}
